<?php
defined('BASEPATH') OR exit('No direct script access allowed');


use GuzzleHttp\Client as HttpClient;


class bigcommerce{

    public $checklink=false;
    private $client;

    public function __construct($param=array())
    {
        $this->apihost="https://api.bigcommerce.com/stores/".$param["store_hash"];
        $this->client = new HttpClient(['timeout'  => 30.0,'headers'=>['Content-Type'=>'application/json','Accept'=>'application/json','X-Auth-Client'=>$param['client_id'],'X-Auth-Token'=>$param['access_token']]]);
        $this->checklink();
    }


    public function change_site($site)
    {
        $extra=json_decode($site["extra"],true);
        $this->apihost="https://api.bigcommerce.com/stores/".$extra["store_hash"];
        $this->client = new HttpClient(['timeout'  => 30.0,'headers'=>['Content-Type'=>'application/json','Accept'=>'application/json','X-Auth-Client'=>$extra['client_id'],'X-Auth-Token'=>$extra['access_token']]]);
        $this->checklink();
    }

    public function updatepro($pid,$newdata)
    {
        $path="/v3/catalog/products/".$pid;
        $new=[];
        if(isset($newdata["title"]))
        {
            $new["name"]=$newdata["title"];
                 
        }
        if(isset($newdata["price"]))
        {
            $new["price"]=$newdata["price"];
        }
        if($new)
        {
            $this->api_query($path,"put",$new);
        }
        return "ok";
    }

    public function getproducts($page)
    {
        $return=["data"=>[]];
        $path="/v3/catalog/products?page=".$page;

        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        if(!$result["data"])
        {
            return $return;
        }
        foreach($result["data"] as $pro)
        {
            $newdata=[
                "id"=>$pro["id"],              
            ];
            $return["data"][]=$newdata;
        }
        return $return;
    }


    public function syncuser($page,$maxuid)
    {
        $return=["data"=>[]];
        $path="/v3/customers?page=".$page;
        if($maxuid>0)
        {
           // $path.="&id:greater=".$maxuid;            
        }
        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        if(!$result["data"])
        {
            return $return;
        }
        foreach($result["data"] as $user)
        {
            $newuser=[
                "id_customer"=>$user["id"],
                "firstname"=>$user["first_name"],
                "lastname"=>$user["last_name"],
                "email"=>$user["email"],
                "date_add"=>date("Y-m-d H:i:s",strtotime($user["date_created"]))
            ];
            $return["data"][]=$newuser;
        }
        return $return;
    }

    public function cancel($oid)
    {
        /*
        $path="/admin/api/2019-04/orders/".$oid."/cancel.json";
        $this->api_query($path,"post");
        $this->refund($oid);        
        */
        return "ok";
    }
    /*
    private function orderinfo($orderid)
    {
        $path="/admin/api/2019-04/orders/{$orderid}.json";
        $info=$this->api_query($path);
        return json_decode($info,true);
    }

    private function refundcalculate($orderid,$orderinfo)
    {
        $path="/admin/api/2019-04/orders/{$orderid}/refunds/calculate.json";
        $data=["refund"=>[
            "currency"=>"USD",
            "shipping"=>["full_refund"=>"true"],
            "refund_line_items"=>[],
        ]];
        
        foreach($orderinfo["order"]["line_items"] as $item)
        {
            $data["refund"]["refund_line_items"][]=[
                "line_item_id"=>$item["id"],
                "quantity"=>$item["quantity"],
                "restock_type"=>"no_restock",
            ];
        }
        $result=$this->api_query($path,"post",$data);
        return json_decode($result,true);
    }

    private function refund($orderid)
    {
        $orderinfo=$this->orderinfo($orderid);
        $path="/admin/api/2019-04/orders/{$orderid}/refunds.json";
        $data=["refund"=>[
            "currency"=>"USD",
            "notify"=>"true",
            "note"=>"out of stock",            
            "shipping"=>["full_refund"=>"true"],
            "refund_line_items"=>[],
            "transactions"=>[]
        ]];
        foreach($orderinfo["order"]["line_items"] as $item)
        {
            $data["refund"]["refund_line_items"][]=[
                "line_item_id"=>$item["id"],
                "quantity"=>$item["quantity"],
                "restock_type"=>"no_restock",
            ];
        }
        $transactions=$this->refundcalculate($orderid,$orderinfo);
        $data["refund"]["transactions"][]=[
            "parent_id"=>$transactions["refund"]["transactions"][0]["parent_id"],
            "amount"=>$transactions["refund"]["transactions"][0]["amount"],
            "kind"=>"refund",
            "gateway"=>$transactions["refund"]["transactions"][0]["gateway"]
        ];
        $result=$this->api_query($path,"post",$data);
        return json_decode($result,true);
    }
    */
    
    public function shiporder($oid,$tracknumber)
    {
        $path="/v2/orders/{$oid}/shipments";
        $data=["tracking_number"=>$tracknumber,"shipping_provider"=>""];
        $shipaddress=$this->getorderaddress($oid);
        $data["order_address_id"]=$shipaddress["id"];
        $products=$this->getorderproducts($oid);
        foreach($products as $pro)
        {
            $data["items"][]=[
                "order_product_id"=>$pro["id"],
                "quantity"=>$pro["quantity"]
            ];
        }
        $this->api_query($path,"post",$data);
        return "ok";
    }


    public function checkwaitorder($waitorder)
    {
        return false;
    }

    private function getorderaddress($oid)
    {
        $path="/v2/orders/{$oid}/shipping_addresses";
        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        return $result[0];
    }

    private function getorderproducts($oid)
    {
        $path="/v2/orders/{$oid}/products";
        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        return $result;
    }

    public function syncorder($page,$maxoid,$status=1)
    {
        $return=["data"=>[]];
        $path="/v2/orders?page=".$page."&status_id=11";
        if($maxoid>0)
        {
            $path.="&min_id=".$maxoid;
        }
        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        if(!$result)
        {
            return $return;
        }
        foreach($result as $order)
        {
            $neworder=[
                "id_order"=>$order["id"],
                "id_customer"=>$order["customer_id"],
                "current_state"=>2,
                "reference"=>$order["id"],
                "total_paid"=>$order["total_inc_tax"],               
                "date_add"=>date("Y-m-d H:i:s",strtotime($order["date_created"])),
                "payment"=>$order["payment_method"],
                "products"=>[],
            ];
            $shipaddress=$this->getorderaddress($order["id"]);
            $address=[
                "firstname"=>$shipaddress["first_name"],
                "lastname"=>$shipaddress["last_name"],
                "phone"=>$shipaddress["phone"],
                "phone_mobile"=>"",
                "name"=>$shipaddress["country"],
                "iso_code"=>$shipaddress["country_iso2"],
                "stname"=>$shipaddress["state"],
                "email"=>$shipaddress["email"],
                "postcode"=>$shipaddress["zip"],
                "stcode"=>$this->statetocode($shipaddress["state"]),
                "city"=>$shipaddress["city"],
                "address1"=>$shipaddress["street_1"],
                "address2"=>isset($shipaddress["street_2"])?$shipaddress["street_2"]:"",
            ];
            $neworder=array_merge($neworder,$address);
            
            $products=$this->getorderproducts($order["id"]);

            foreach($products as $item)
            {
                $optionval="";
                //print_r($item["product_options"]);
                foreach($item["product_options"] as $option)
                {
                    $optionval.=" ".$option["display_name"].":".$option["display_value"];
                }
                $hand=$this->getproduct($item["product_id"],"custom_url");
                $newp=[
                    "product_name"=>$item["name"],
                    "variant"=>$optionval,
                    "product_quantity"=>$item["quantity"],
                    "product_price"=>$item["base_price"],
                    "product_id"=>$item["product_id"],
                    "handler"=>$hand["data"]["custom_url"]["url"],
                ];
                $neworder["products"][]=$newp;
            }
            $return["data"][]=$neworder;
        }
        return $return;
    }

    private function getproduct($pid,$fields="")
    {
        $path="/v3/catalog/products/{$pid}";
        if($fields)
        {
            $path.="?include_fields=".$fields;
        }
        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        return $result;
    }

    public function delproduct($pro)
    {
        $pid=$pro["rid"];
        $path="/v3/catalog/products?id=".$pid;
        $this->api_query($path,"delete");
        return "ok";
    }

    private function statetocode($state)
    {
        $info=[
            "Alabama"=>"AL",
            "Alaska"=>"AK",
            "Arizona"=>"AZ",
            "Arkansas"=>"AR",
            "Armed Forces America"=>"AA",
            "Armed Forces Europe"=>"AE",
            "Armed Forces Pacific"=>"AP",
            "California"=>"CA",
            "Colorado"=>"CO",
            "Connecticut"=>"CT",
            "Delaware"=>"DE",
            "District of Columbia"=>"DC",
            "Florida"=>"FL",
            "Georgia"=>"GA",
            "Hawaii"=>"HI",
            "Idaho"=>"ID",
            "Illinois"=>"IL",
            "Indiana"=>"IN",
            "Iowa"=>"IA",
            "Kansas"=>"KS",
            "Kentucky"=>"KY",
            "Louisiana"=>"LA",
            "Maine"=>"ME",
            "Maryland"=>"MD",
            "Massachusetts"=>"MA",
            "Michigan"=>"MI",
            "Minnesota"=>"MN",
            "Mississippi"=>"MS",
            "Missouri"=>"MO",
            "Montana"=>"MT",
            "Nebraska"=>"NE",
            "Nevada"=>"NV",
            "New Hampshire"=>"NH",
            "New Jersey"=>"NJ",
            "New Mexico"=>"NM",
            "New York"=>"NY",
            "North Carolina"=>"NC",
            "North Dakota"=>"ND",
            "Ohio"=>"OH",
            "Oklahoma"=>"OK",
            "Oregon"=>"OR",
            "Pennsylvania"=>"PA",
            "Rhode Island"=>"RI",
            "South Carolina"=>"SC",
            "South Dakota"=>"SD",
            "Tennessee"=>"TN",
            "Texas"=>"TX",
            "Utah"=>"UT",
            "Vermont"=>"VT",
            "Virginia"=>"VA",
            "Washington"=>"WA",
            "West Virginia"=>"WV",
            "Wisconsin"=>"WI",
            "Wyoming"=>"WY",
        ];
        foreach($info as $k=>$v)
        {
            if(strtolower($k)==strtolower($state))
            {
                return $v;
            }
        }
        return $state;
    }


    private function addbaseproduct($product)
    {
        $compare_ratio=[1.3,1.35,1.4,1.45,1.5,1.55,1.6,1.65,1.7];
        shuffle($compare_ratio);
        $compareprice=sprintf("%.2f",$product["price"]*$compare_ratio[0]);
        $path="/v3/catalog/products";
        $data=[
            "name"=>$product["name"],
            "meta_title"=>$product["name"],
            "meta_desc"=>$product["meta"]?$product["meta"]:$product["name"],
            "type"=>"physical",
            "sku"=>$product["sku"],
            "price"=>$product["price"],
            "retail_price"=>$compareprice,
            "description"=>$product["desc"]?$product["desc"]:$product["name"],
            "categories"=>[$product["rid"]],
            "custom_fields"=>[],
            "weight"=>5,
        ];
        if($product["vendor"])
        {
            $data["brand_name"]=$product["vendor"];
        }
        $result=$this->api_query($path,"post",$data);
        $result=json_decode($result,true);
        if(isset($result["error"]) && $result["error"]==1)
        {
            return;
            /*
            if(strstr($result["msg"],"The product name is a duplicate"))
            {
                $product["name"].=" ".mt_rand(1000,9999);
                return $this->addbaseproduct($product);    
            }
            else
            {
                return;
            }
            */

        }
        else
        {
            return $result["data"]["id"];
        }
    }

    private function addproductimages($product,$pid)
    {
        if($product["thumb"]&&count($product["thumb"])>=1)
        {

            $path="/v3/catalog/products/{$pid}/images";
            foreach($product["thumb"] as $k=>$v)
            {
                $data=[                    
                    "product_id"=>$pid,
                    "image_url"=>$this->imgurl($v),
                    "description"=>$product["name"],
                ];
                if($k==0)
                {
                    $data["is_thumbnail"]=true;
                }
                $result=$this->api_query($path,"post",$data);
            }
        }
    }

    public function addproduct($product)
    {
        if(empty($product["name"]))
        {
            return 0;
        }
        //1.add base product
        $pid=$this->addbaseproduct($product);


        if($pid>0)
        {
            //2.add product image
            $this->addproductimages($product,$pid);

            //3.conver product options
            if(isset($product["option"]["vv"]) && count($product["option"]["vv"])>0)
            {
                $compare_ratio=[1.3,1.35,1.4,1.45,1.5,1.55,1.6,1.65,1.7];
                shuffle($compare_ratio);
                $product["option"]=$this->covertbigoption($product["option"],$pid);
                if(empty($product["option"]))
                {
                    return 0;
                }

                //4.add product variant
                $path="/v3/catalog/products/{$pid}/variants";
                
                if(count($product["option"]["vv"])==1)
                {
                    $option=$product["option"]["vv"][0];
                    foreach($option["value"] as $k=>$v)
                    {


                        $data=[
                            "sku"=>$product["sku"]."_".$k,
                            "product_id"=>$pid,
                            "option_values"=>[
                                [
                                    "option_id"=>$option["bigid"],
                                    "id"=>$option["bigopid"][$k]
                                ]
                            ]
                        ];           
                        if(isset($product["vc"]) && isset($product["vc"][$k]) && $product["vc"][$k])
                        {
                            $price=$product["vc"][$k];
                            $retail_price=sprintf("%.2f",$price*$compare_ratio[0]);
                            $data['price']=$price;
                            $data['retail_price']=$retail_price;
                        }

                        $result=$this->api_query($path,"post",$data);
                        $json=$result;
                        $result=json_decode($result,true);                        
                        if(isset($option["image"])&&isset($option["image"][$k])&&$option["image"][$k])
                        {
                            $vaid=$result["data"]["id"];
                            if(empty($vaid))
                            {
                                echo $json;
                            }
                            else
                            {
                                $path2="/v3/catalog/products/{$pid}/variants/{$vaid}/image";
                                $data=[
                                    "image_url"=>$this->imgurl($option["image"][$k])
                                ];
                                $this->api_query($path2,"post",$data);
                            }                           
                        }
                    }

                }
                else if(count($product["option"]["vv"])==2)
                {
                    $option1=$product["option"]["vv"][0];
                    $option2=$product["option"]["vv"][1];
                    $icount=0;
                    foreach($option1["value"] as $k=>$v)
                    {
                        foreach($option2["value"] as $j=>$w)
                        {
                            $data=[
                                "sku"=>$product["sku"]."_".$icount,
                                "product_id"=>$pid,
                                "option_values"=>[
                                    [
                                        "option_id"=>$option1["bigid"],
                                        "id"=>$option1["bigopid"][$k]
                                    ],
                                    [
                                        "option_id"=>$option2["bigid"],
                                        "id"=>$option2["bigopid"][$j]
                                    ]
                                ]
                            ];
                            if(isset($product["vc"]) && isset($product["vc"][$icount]) && $product["vc"][$icount])
                            {
                                $price=$product["vc"][$icount];
                                $retail_price=sprintf("%.2f",$price*$compare_ratio[0]);
                                $data['price']=$price;
                                $data['retail_price']=$retail_price;
                            }

                            $result=$this->api_query($path,"post",$data);
                            $json=$result;
                            $result=json_decode($result,true);
                            if(isset($result["error"]) && $result["error"]==1)
                            {
                                if(strstr($result["msg"],"Exceeds the maximum allowed number of SKUs per product"))
                                {
                                    continue;
                                }
                            }
                            $image_url="";
                            if(isset($option1["image"])&&isset($option1["image"][$k])&&$option1["image"][$k])
                            {
                                $image_url=$this->imgurl($option1["image"][$k]);
                            }
                            if(isset($option2["image"])&&isset($option2["image"][$j])&&$option2["image"][$j])
                            {
                                $image_url=$this->imgurl($option2["image"][$j]);
                            }

                            if($image_url)
                            {
                                $vaid=$result["data"]["id"];

                                if(empty($vaid))
                                {
                                    echo $json;
                                }
                                else
                                {
                                
                                    $path2="/v3/catalog/products/{$pid}/variants/{$vaid}/image";
                                    $data=[
                                        "image_url"=>$image_url
                                    ];
                                    $this->api_query($path2,"post",$data);
                                }
                            }                                 
                            $icount++;
                        }
                    }
                }

            }
            
        }
        return ["rid"=>$pid];
    }

    private function covertbigoption($option,$pid)
    {
        $path="/v3/catalog/products/".$pid."/options";
        foreach($option["vv"] as $k=>$val)
        {
            $data=[
                "product_id"=>$pid,
                "name"=>$val["name"],
                "display_name"=>$val["name"],
                "type"=>"rectangles",
                "option_values"=>[]
            ];
            $exitval=[];
            foreach($val["value"] as $j=>$v)
            {
                $v=strtolower($v);                
                if(in_array($v,$exitval))
                {
                    $opval=$v." ".$j;
                    $exitval[]=$opval;
                }
                else
                {
                    $exitval[]=$v;
                    $opval=$v;
                }
                $data["option_values"][]=[
                    "label"=>strval($opval),
                    "sort_order"=>$j,
                    "is_default"=>$j==0?true:false,
                ];
            }
            $result=$this->api_query($path,"post",$data);
            $result=json_decode($result,true);

            if(isset($result["error"]) && $result["error"]==1)
            {
                return false;
            }
            $option["vv"][$k]["bigid"]=$result["data"]["id"];
            foreach($result["data"]["option_values"] as $vv)
            {
                $option["vv"][$k]["bigopid"][]=$vv["id"];
            }
        }
        return $option;
    }

    private function imgurl($url)
    {
        if(substr($url,0,4)!="http")
        {
            $url="http:".$url;
        }

        $url = str_replace("ae01.alicdn.com","picture.qdbeauty.com",$url);
        $url = str_replace("cdn.shopify.com","image.shoptopdress.com",$url);
        $url = str_replace("_50x50","_640x640",$url);
        if(strstr($url,"_640x640"))
        {
            $tmp=explode("_640x640",$url);
            return $tmp[0];
        }
        else
        {
            return $url;
        }        
    }

    /* 分类操作  */
    public function synccatrgory($category)
    {
        $result=[];
        foreach($category as $cat)
        {
            if($cat["rid"]>0)
            {
                $this->updatecollect($cat["rid"],$cat["name"]);
            }
            else
            {
                $rid=$this->publishcat($cat["name"]);
                $result[$cat["id"]]=$rid;
            }
        }
        return $result;
    }

    public function delcatgory($cat)
    {
        $rid=$cat["rid"];
        if($rid>0)
        {
            $path="/v3/catalog/categories/".$rid;
            $this->api_query($path,"delete");
        }
    }

    private function updatecollect($rid,$name)
    {
        $path="/v3/catalog/categories/".$rid;
        $data=["name"=>$name];
        $this->api_query($path,"put",$data);        
    }

    private function publishcat($name)
    {
        $path="/v3/catalog/categories?name=".$name;
        $result=$this->api_query($path,"get");
        $result=json_decode($result,true);
        if($result["data"] && count($result["data"])>0)
        {
            return $result["data"][0]["id"];
        }
        $path="/v3/catalog/categories";
        $data=["parent_id"=>0,"name"=>$name];
        $result=$this->api_query($path,"post",$data);
        $result=json_decode($result,true);
        return $result["data"]["id"];
    }

    private function api_query($path,$querymethod="get",$jsondata=[])
    {
        $host=$this->apihost;
        $url=$host.$path;
        $errorcount=2;
        $maxerror=$errorcount;
        while($errorcount>0)
        {
            try
            {
                while(1)
                {
                    switch($querymethod)
                    {
                        case "get":
                            $res=$this->client->request('GET', $url);
                            $html_doc=$res->getBody();
                            break;
                        case "post":
                            $res=$this->client->request('POST', $url, [
                                'json' => $jsondata
                            ]);
                            $html_doc=$res->getBody();
                            break;
                        case "put":
                            $res=$this->client->request('PUT', $url, [
                                'json' => $jsondata
                            ]);
                            $html_doc=$res->getBody();                        
                            break;
                        case "delete":
                            $res=$this->client->request('DELETE', $url);
                            $html_doc=$res->getStatusCode();
                            break;
                    }

                    if(strstr($html_doc,"Exceeded 2 calls per second"))
                    {
                        sleep(1);
                        continue;
                    }
                    else
                    {
                        return $html_doc;                        
                    }
                }
            }   
            catch (Exception $e) {
                echo $url."\n";
                echo '请求异常，重试...'.($maxerror-$errorcount+1).'/'.$maxerror.'...'."\n";
                echo $e->getMessage();
                echo "\n";
                $errorcount--;
                if($errorcount==0)
                {
                    if(strstr($e->getMessage(),"429 Too Many Requests"))
                    {
                        sleep(5);
                        return $this->api_query($path,$querymethod,$jsondata);
                    }
                    $this->savelog($e->getMessage(),$jsondata);             
                    $rmsg=["error"=>1,"msg"=>$e->getMessage()];       
                    return json_encode($rmsg);                    
                }
            }
        }
    }

    private function savelog($msg,$datainfo)
    {
        $rawmsg="-----time:".date("H:i:s")."---------\n";
        $rawmsg.=$msg."\n";
        $rawmsg.=json_encode($datainfo)."\n\n";
        file_put_contents("./logs/".date("Ymd")."bigc.text",$rawmsg,FILE_APPEND | LOCK_EX);
    }

    private function checklink()
    {
        $path="/v2/store";
        $data=$this->api_query($path);
        $result=json_decode($data,true);
        if(isset($result["id"]) && $result["id"])
        {
            $this->checklink=true;
        }
    }
    
}